home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
nethack.lha
/
nethack-3.1
/
src
/
pager.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-22
|
17KB
|
663 lines
/* SCCS Id: @(#)pager.c 3.1 92/09/01 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
/* This file contains the command routines dowhatis() and dohelp() and */
/* a few other help related facilities */
#include "hack.h"
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
static boolean FDECL(is_swallow_sym, (UCHAR_P));
static int FDECL(append_str, (char *, const char *));
static void FDECL(lookat, (int, int, char *));
static void FDECL(checkfile, (char *, BOOLEAN_P));
static int FDECL(do_look, (BOOLEAN_P));
static char NDECL(help_menu);
#ifdef PORT_HELP
extern void NDECL(port_help);
#endif
/* Returns "true" for characters that could represent a monster's stomach. */
static boolean
is_swallow_sym(c)
uchar c;
{
int i;
for (i = S_sw_tl; i <= S_sw_br; i++)
if (showsyms[i] == c) return TRUE;
return FALSE;
}
/*
* Append new_str to the end of buf if new_str doesn't already exist as
* a substring of buf. Return 1 if the string was appended, 0 otherwise.
* It is expected that buf is of size BUFSZ.
*/
static int
append_str(buf, new_str)
char *buf;
const char *new_str;
{
int space_left; /* space remaining in buf */
if (strstri(buf, new_str)) return 0;
space_left = BUFSZ - strlen(buf) - 1;
(void) strncat(buf, " or ", space_left);
(void) strncat(buf, new_str, space_left - 4);
return 1;
}
/*
* Return the name of the glyph found at (x,y).
*/
static void
lookat(x, y, buf)
int x, y;
char *buf;
{
register struct monst *mtmp;
struct trap *trap;
register char *s, *t;
int glyph;
buf[0] = 0;
glyph = glyph_at(x,y);
if (u.ux == x && u.uy == y && canseeself()) {
Sprintf(buf, "%s called %s",
#ifdef POLYSELF
u.mtimedone ? mons[u.umonnum].mname :
#endif
player_mon()->mname, plname);
}
else if (u.uswallow) {
/* all locations when swallowed other than the hero are the monster */
Sprintf(buf, "interior of %s",
Blind ? "a monster" : a_monnam(u.ustuck));
}
else if (glyph_is_monster(glyph)) {
bhitpos.x = x;
bhitpos.y = y;
mtmp = m_at(x,y);
if(mtmp != (struct monst *) 0) {
register boolean hp = (mtmp->data == &mons[PM_HIGH_PRIEST]);
Sprintf(buf, "%s%s%s",
(!hp && mtmp->mtame && !Hallucination) ? "tame " :
(!hp && mtmp->mpeaceful && !Hallucination) ?
"peaceful " : "",
(hp ? "high priest" : l_monnam(mtmp)),
u.ustuck == mtmp ?
#ifdef POLYSELF
(u.mtimedone ? ", being held" :
#endif
", holding you"
#ifdef POLYSELF
)
#endif
: "");
}
}
else if (glyph_is_object(glyph)) {
struct obj *otmp = vobj_at(x,y);
if(otmp == (struct obj *) 0 || otmp->otyp != glyph_to_obj(glyph)) {
if(glyph_to_obj(glyph) != STRANGE_OBJECT) {
otmp = mksobj(glyph_to_obj(glyph), FALSE, FALSE);
if(otmp->oclass == GOLD_CLASS)
otmp->quan = 2L; /* to force pluralization */
Strcpy(buf, distant_name(otmp, xname));
dealloc_obj(otmp);
}
} else
Strcpy(buf, distant_name(otmp, xname));
}
else if (glyph_is_trap(glyph)) {
if (trap = t_at(x, y)) {
if (trap->ttyp == WEB)
Strcpy(buf, "web");
else {
Strcpy(buf, traps[ Hallucination ?
rn2(TRAPNUM-3)+3 : trap->ttyp]);
/* strip leading garbage */
for (s = buf; *s && *s != ' '; s++) ;
if (*s) ++s;
for (t = buf; *t++ = *s++; ) ;
}
}
}
else if(!glyph_is_cmap(glyph))
Strcpy(buf,"dark part of a room");
else switch(glyph_to_cmap(glyph)) {
case S_altar:
if(!In_endgame(&u.uz))
Sprintf(buf, "%s altar",
align_str(Amask2align(levl[x][y].altarmask & ~AM_SHRINE)));
else Sprintf(buf, "aligned altar");
break;
case S_ndoor:
if((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN)
Strcpy(buf,"broken door");
else
Strcpy(buf,"doorway");
break;
default:
Strcpy(buf,defsyms[glyph_to_cmap(glyph)].explanation);
break;
}
}
/*
* Look in the "data" file for more info. Called if the user typed in the
* whole name (user_typed_name == TRUE), or we've found a possible match
* with a character/glyph and flags.help is TRUE.
*
* NOTE: when (user_typed_name == FALSE), inp is considered read-only and
* must not be changed directly, e.g. via lcase(). Permitted are
* functions, e.g. makesingular(), which operate on a copy of inp.
*/
static void
checkfile(inp, user_typed_name)
char *inp;
boolean user_typed_name;
{
FILE *fp;
char buf[BUFSZ];
char *ep;
long txt_offset;
boolean found_in_file = FALSE;
fp = fopen_datafile(DATAFILE, "r");
if (!fp) {
pline("Cannot open data file!");
return;
}
if (!strncmp(inp, "interior of ", 12))
inp += 12;
if (!strncmp(inp, "a ", 2))
inp += 2;
else if (!strncmp(inp, "an ", 3))
inp += 3;
else if (!strncmp(inp, "the ", 4))
inp += 4;
if (!strncmp(inp, "tame ", 5))
inp += 5;
else if (!strncmp(inp, "peaceful ", 9))
inp += 9;
if (!strncmp(inp, "invisible ", 10))
inp += 10;
/* Make sure the name is non-empty. */
if (*inp) {
/* adjust the input to remove "named " and convert to lower case */
char *alt = 0; /* alternate description */
if ((ep = strstri(inp, " named ")) != 0)
alt = ep + 7;
else
ep = strstri(inp, " called ");
if (ep) *ep = '\0';
if (user_typed_name)
(void) lcase(inp);
/*
* If the object is named, then the name is the alternate description;
* otherwise, the result of makesingular() applied to the name is. This
* isn't strictly optimal, but named objects of interest to the user
* will usually be found under their name, rather than under their
* object type, so looking for a singular form is pointless.
*/
if (!alt)
alt = makesingular(inp);
else
if (user_typed_name)
(void) lcase(alt);
/* skip first record; read second */
txt_offset = 0L;
if (!fgets(buf, BUFSZ, fp) || !fgets(buf, BUFSZ, fp)) {
impossible("can't read 'data' file");
(void) fclose(fp);
return;
} else if (sscanf(buf, "%8lx\n", &txt_offset) < 1 || txt_offset <= 0)
goto bad_data_file;
/* look for the appropriate entry */
while (fgets(buf,BUFSZ,fp)) {
if (*buf == '.') break; /* we passed last entry without success */
if (!digit(*buf)) {
if (!(ep = index(buf, '\n'))) goto bad_data_file;
*ep = 0;
if (pmatch(buf, inp) || (alt && pmatch(buf, alt))) {
found_in_file = TRUE;
break;
}
}
}
}
if(found_in_file) {
long entry_offset;
int entry_count;
int i;
/* skip over other possible matches for the info */
do {
if (!fgets(buf, BUFSZ, fp)) goto bad_data_file;
} while (!digit(*buf));
if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) {
bad_data_file: impossible("'data' file in wrong format");
(void) fclose(fp);
return;
}
if (user_typed_name || yn("More info?") == 'y') {
winid datawin;
if (fseek(fp, txt_offset + entry_offset, SEEK_SET) < 0) {
pline("? Seek error on 'data' file!");
(void) fclose(fp);
return;
}
datawin = create_nhwindow(NHW_TEXT);
for (i = 0; i < entry_count; i++) {
if (!fgets(buf, BUFSZ, fp)) goto bad_data_file;
if ((ep = index(buf, '\n')) != 0) *ep = 0;
if (index(buf+1, '\t') != 0) (void) tabexpand(buf+1);
putstr(datawin, 0, buf+1);
}
display_nhwindow(datawin, FALSE);
destroy_nhwindow(datawin);
}
} else if (user_typed_name)
pline("I don't have any information on those things.");
(void) fclose(fp);
}
static int
do_look(quick)
boolean quick; /* use cursor && don't search for "more info" */
{
char out_str[BUFSZ], look_buf[BUFSZ];
const char *firstmatch = 0;
int i;
int sym; /* typed symbol or converted glyph */
int found; /* count of matching syms found */
coord cc; /* screen pos of unknown glyph */
boolean save_verbose; /* saved value of flags.verbose */
boolean from_screen; /* question from the screen */
boolean need_to_look; /* need to get explan. from glyph */
static const char *mon_interior = "the interior of a monster";
#ifdef GCC_WARN
sym = 0;
#endif
if (quick) {
from_screen = TRUE; /* yes, we want to use the cursor */
} else {
i = ynq("Specify unknown object by cursor?");
if (i == 'q') return 0;
from_screen = (i == 'y');
}
if (from_screen) {
cc.x = u.ux;
cc.y = u.uy;
} else {
getlin("Specify what? (type the word)", out_str);
if (out_str[0] == '\033')
return 0;
if (out_str[1]) { /* user typed in a complete string */
checkfile(out_str, TRUE);
return 0;
}
sym = out_str[0];
}
/* Save the verbose flag, we change it later. */
save_verbose = flags.verbose;
flags.verbose = flags.verbose && !quick;
/*
* The user typed one letter, or we're identifying from the screen.
*/
do {
/* Reset some variables. */
need_to_look = FALSE;
found = 0;
out_str[0] = '\0';
if (from_screen) {
int glyph; /* glyph at selected position */
if (flags.verbose)
pline("Please move the cursor to an unknown object.");
else
pline("Pick an object.");
getpos(&cc, FALSE, "an unknown object");
if (cc.x < 0) {
flags.verbose = save_verbose;
return 0; /* done */
}
flags.verbose = FALSE; /* only print long question once */
/* Convert the glyph at the selected position to a symbol. */
glyph = glyph_at(cc.x,cc.y);
if (glyph_is_cmap(glyph)) {
sym = showsyms[glyph_to_cmap(glyph)];
} else if (glyph_is_trap(glyph)) {
sym = showsyms[(glyph_to_trap(glyph) == WEB) ? S_web : S_trap];
} else if (glyph_is_object(glyph)) {
sym = oc_syms[objects[glyph_to_obj(glyph)].oc_class];
} else if (glyph_is_monster(glyph)) {
sym = monsyms[mons[glyph_to_mon(glyph)].mlet];
} else if (glyph_is_swallow(glyph)) {
sym = showsyms[glyph_to_swallow(glyph)+S_sw_tl];
} else {
impossible("do_look: bad glyph %d at (%d,%d)",
glyph, (int)cc.x, (int)cc.y);
sym = ' ';
}
}
/*
* Check all the possibilities, saving all explanations in a buffer.
* When all have been checked then the string is printed.
*/
/* Check for monsters */
for (i = 0; i < MAXMCLASSES; i++) {
if (sym == (from_screen ? monsyms[i] : def_monsyms[i])) {
need_to_look = TRUE;
if (!found) {
Sprintf(out_str, "%c %s", sym, an(monexplain[i]));
firstmatch = monexplain[i];
found++;
} else {
found += append_str(out_str, an(monexplain[i]));
}
}
}
/*
* Special case: if identifying from the screen, and we're swallowed,
* and looking at something other than our own symbol, then just say
* "the interior of a monster".
*/
if (u.uswallow && from_screen && is_swallow_sym((uchar) sym)) {
if (!found) {
Sprintf(out_str, "%c %s", sym, mon_interior);
firstmatch = mon_interior;
} else {
found += append_str(out_str, mon_interior);
}
need_to_look = TRUE;
}
/* Now check for objects */
for (i = 1; i < MAXOCLASSES; i++) {
if (sym == (from_screen ? oc_syms[i] : def_oc_syms[i])) {
need_to_look = TRUE;
if (!found) {
Sprintf(out_str, "%c %s", sym, an(objexplain[i]));
firstmatch = objexplain[i];
found++;
} else {
found += append_str(out_str, an(objexplain[i]));
}
}
}
/* Now check for graphics symbols */
for (i = 0; i < MAXPCHARS; i++) {
if (sym == (from_screen ? showsyms[i] : defsyms[i].sym) &&
(*defsyms[i].explanation)) {
if (!found) {
Sprintf(out_str, "%c %s",
sym, an(defsyms[i].explanation));
firstmatch = defsyms[i].explanation;
found++;
} else if (!u.uswallow) {
found += append_str(out_str, an(defsyms[i].explanation));
}
if (i == S_altar || i == S_trap || i == S_web)
need_to_look = TRUE;
}
}
/*
* If we are looking at the screen, follow multiple posibilities or
* an ambigious explanation by something more detailed.
*/
if (from_screen) {
if (found > 1 || need_to_look) {
lookat(cc.x, cc.y, look_buf);
firstmatch = look_buf;
if (*firstmatch) {
char temp_buf[BUFSZ];
Sprintf(temp_buf, " (%s)", firstmatch);
(void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1);
found = 1; /* we have something to look up */
}
}
}
/* Finally, print out our explanation. */
if (found) {
pline(out_str);
/* check the data file for information about this thing */
if (found == 1 && !quick && flags.help) {
char temp_buf[BUFSZ];
Strcpy(temp_buf, firstmatch);
checkfile(temp_buf, FALSE);
}
} else {
pline("I've never heard of such things.");
}
} while (from_screen && !quick);
flags.verbose = save_verbose;
return 0;
}
int
dowhatis()
{
return do_look(FALSE);
}
int
doquickwhatis()
{
return do_look(TRUE);
}
int
doidtrap()
{
register struct trap *trap;
register int x,y;
if(!getdir(NULL)) return 0;
x = u.ux + u.dx;
y = u.uy + u.dy;
for(trap = ftrap; trap; trap = trap->ntrap)
if(trap->tx == x && trap->ty == y && trap->tseen) {
if(u.dz) {
if(u.dz < 0 && trap->ttyp == TRAPDOOR)
continue;
if(u.dz > 0 && trap->ttyp == ROCKTRAP)
continue;
}
pline("That is a%s.",
traps[ Hallucination ? rn1(TRAPNUM-3, 3) :
trap->ttyp]);
return 0;
}
pline("I can't see a trap there.");
return 0;
}
int
dowhatdoes()
{
FILE *fp;
char bufr[BUFSZ+6];
register char *buf = &bufr[6], *ep, q, ctrl, meta;
fp = fopen_datafile(CMDHELPFILE, "r");
if (!fp) {
pline("Cannot open data file!");
return 0;
}
#if defined(UNIX) || defined(VMS)
introff();
#endif
q = yn_function("What command?", NULL, '\0');
#if defined(UNIX) || defined(VMS)
intron();
#endif
ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0);
meta = ((0x80 & q) ? (0x7f & q) : 0);
while(fgets(buf,BUFSZ,fp))
if ((ctrl && *buf=='^' && *(buf+1)==ctrl) ||
(meta && *buf=='M' && *(buf+1)=='-' && *(buf+2)==meta) ||
*buf==q) {
ep = index(buf, '\n');
if(ep) *ep = 0;
if (ctrl && buf[2] == '\t'){
buf = bufr + 1;
(void) strncpy(buf, "^? ", 8);
buf[1] = ctrl;
} else if (meta && buf[3] == '\t'){
buf = bufr + 2;
(void) strncpy(buf, "M-? ", 8);
buf[2] = meta;
} else if(buf[1] == '\t'){
buf = bufr;
buf[0] = q;
(void) strncpy(buf+1, " ", 7);
}
pline("%s", buf);
(void) fclose(fp);
return 0;
}
pline("I've never heard of such commands.");
(void) fclose(fp);
return 0;
}
/* data for help_menu() */
static const char *help_menu_items[] = {
"Information available:",
"",
"a. Long description of the game and commands.",
"b. List of game commands.",
"c. Concise history of NetHack.",
"d. Info on a character in the game display.",
"e. Info on what a given key does.",
"f. List of game options.",
"g. Longer explanation of game options.",
"h. List of extended commands.",
"i. The NetHack license.",
#ifdef PORT_HELP
"j. %s-specific help and commands.",
#endif
#ifdef WIZARD
# ifdef PORT_HELP
# define WIZHLP_SLOT 12 /* assumed to be next to last by code below */
"k. List of wizard-mode commands.",
# else
# define WIZHLP_SLOT 11 /* assumed to be next to last by code below */
"j. List of wizard-mode commands.",
# endif
#endif
"",
NULL
};
static char
help_menu()
{
winid tmpwin = create_nhwindow(NHW_MENU);
#ifdef PORT_HELP
char helpbuf[QBUFSZ];
#endif
char hc;
register int i;
start_menu(tmpwin);
#ifdef WIZARD
if (!wizard) help_menu_items[WIZHLP_SLOT] = "",
help_menu_items[WIZHLP_SLOT+1] = NULL;
#endif
for (i = 0; help_menu_items[i]; i++)
#ifdef PORT_HELP
/* port-specific line has a %s in it for the PORT_ID */
if (index(help_menu_items[i], '%')) {
Sprintf(helpbuf, help_menu_items[i], PORT_ID);
add_menu(tmpwin, helpbuf[0], 0, helpbuf);
} else
#endif
{
add_menu(tmpwin, i ? *help_menu_items[i] : 0, 0,
help_menu_items[i]);
}
end_menu(tmpwin, '\033', "\033", "Select one item or ESC: ");
hc = select_menu(tmpwin);
destroy_nhwindow(tmpwin);
return hc;
}
int
dohelp()
{
char hc = help_menu();
if (!index(quitchars, hc)) {
switch(hc) {
case 'a': display_file(HELP, TRUE); break;
case 'b': display_file(SHELP, TRUE); break;
case 'c': (void) dohistory(); break;
case 'd': (void) dowhatis(); break;
case 'e': (void) dowhatdoes(); break;
case 'f': option_help(); break;
case 'g': display_file(OPTIONFILE, TRUE); break;
case 'h': (void) doextlist(); break;
case 'i': display_file(LICENSE, TRUE); break;
#ifdef PORT_HELP
case 'j': port_help(); break;
# ifdef WIZARD
case 'k': display_file(DEBUGHELP, TRUE); break;
# endif
#else
# ifdef WIZARD
case 'j': display_file(DEBUGHELP, TRUE); break;
# endif
#endif
}
}
return 0;
}
int
dohistory()
{
display_file(HISTORY, TRUE);
return 0;
}
/*pager.c*/